package org.example.jida;

import cn.com.jit.assp.dsign.DSign;
import cn.com.jit.ida.util.pki.encoders.Base64;
import com.itextpdf.text.io.RASInputStream;
import com.itextpdf.text.io.RandomAccessSourceFactory;
import com.itextpdf.text.pdf.*;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.text.DateFormat;
import java.text.DateFormatSymbols;
import java.text.SimpleDateFormat;
import java.util.Locale;

public class JiDaSigUtil {
    private static final String OUT_DIR = "target/test-output/";

    static final String configPath = "D:/2.properties";

    public static byte[] JITSign(InputStream content) throws IOException {
        DSign signDs = new DSign();
        signDs.initConfig(configPath);
        /*********************************detachSign签名**********************************/
        String signString = signDs.detachSign("", JiDaSigUtil.toByteArray(content));
        //System.out.println("文件签名成功！:"+signString);
        return Base64.decode(signString);
    }


    /**
     * itext 进行pdf签名验证(通过修原码方式进行pdf元数据获取)
     * todo 可以尝试使用 signatureUtil 去解析数据
     *
     * @param signFileNameIn 签名文件
     * @throws IOException
     */
    public static void itextVerifyEdit(String signFileNameIn) throws IOException {
        PdfReader reader = new PdfReader(signFileNameIn);
        byte[] temp = toByteArray(Files.newInputStream(Paths.get(signFileNameIn)));
        AcroFields af = reader.getAcroFields();
        byte[] originData = null;
        byte[] signReturn = null;
        for (String name : af.getSignatureNames()) {
            // 获取源数据
            PdfDictionary v = af.getSignatureDictionary(name);
            PdfArray b = v.getAsArray(PdfName.BYTERANGE);
            long[] gaps = b.asLongArray();
            int[] range = {(int) gaps[0], (int) gaps[1], (int) gaps[2], (int) gaps[3]};
            //获取原文 此处是buffer1,看加密过程中决定是否改buffer1+buffer3
            originData = new byte[range[1] + range[3]];
            System.arraycopy(temp, 0, originData, 0, range[1]);
            System.arraycopy(temp, range[2], originData, range[1], range[3]);
            //System.out.println("Base64.encode(buffer1) = " + Arrays.toString(Base64.encode(buffer1)));
            // 获取签名值
            PdfDictionary pdfDictionary = af.getSignatureDictionary(name);
            PdfString contents = pdfDictionary.getAsString(PdfName.CONTENTS);//签名值
            signReturn = Base64.encode(contents.getBytes());
            System.out.println("验证contents.getBytes() = " + new String(Base64.encode(contents.getBytes())));
        }
        //吉大验证
        JitByteVerify(originData, signReturn);
    }


    /**
     * itext 进行pdf签名验证 （通过原始方式获取pdf原数据-未修改源代码）
     * todo 可以尝试使用 signatureUtil 去解析数据
     *
     * @param signFileNameIn 签名文件
     * @throws IOException
     */
    public static void itextVerifyOriginal(String signFileNameIn) throws IOException {
        PdfReader reader = new PdfReader(signFileNameIn);
        AcroFields af = reader.getAcroFields();
        byte[] originData = null;
        byte[] signReturn = null;
        for (String name : af.getSignatureNames()) {
            PdfDictionary pdfDictionary = af.getSignatureDictionary(name);
            //获取原始数据
            RandomAccessFileOrArray rf = reader.getSafeFile();
            PdfArray b = pdfDictionary.getAsArray(PdfName.BYTERANGE);
            InputStream rg = new RASInputStream(new RandomAccessSourceFactory().createRanged(rf.createSourceView(), b.asLongArray()));
            originData = JiDaSigUtil.toByteArray(rg);
            // 获取签名值
            PdfString contents = pdfDictionary.getAsString(PdfName.CONTENTS);//签名值
            signReturn = Base64.encode(contents.getBytes());
            System.out.println("验证contents.getBytes() = " + new String(Base64.encode(contents.getBytes())));
        }
        //吉大验证
        JitByteVerify(originData, signReturn);
    }

    /**
     * pdfbox 进行pdf签名验证
     *
     * @param signFileNameIn
     * @throws IOException
     */
    public static void pdfBoxVerify(String signFileNameIn) throws IOException {
        FileInputStream signedPDF = new FileInputStream(signFileNameIn);


        byte[] bytes = JiDaSigUtil.toByteArray(signedPDF);
        byte[] origPDF = null;
        byte[] signature = null;
        try {
            PDDocument document = Loader.loadPDF(bytes);
            PDSignature signatureDictionaries = document.getSignatureDictionaries().get(0);
            signature = signatureDictionaries.getContents();//签名数据  获取到的数据直接就可以使用，用来进行数据签名验证

            //System.out.println("signature.length = " + signature.length);
            //signature = Arrays.copyOfRange(signature, 0, 1148);
            signature = Base64.encode(signature);

            System.out.println("Hex.getString(signature) = " + new String(signature));//这里会变成与pdf文件中的内容完全一致
            //System.out.println("new String(signature) = " + new String(signature));//这个值就是签名的16进制字符串内容

            int[] byteRange = signatureDictionaries.getByteRange();
            int len = byteRange[1] + byteRange[3];
            origPDF = new byte[len];
            System.arraycopy(bytes,0,origPDF,0,byteRange[1]);
            System.arraycopy(bytes,byteRange[2],origPDF,byteRange[1],byteRange[3]);

        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        JitByteVerify(origPDF, signature);
    }


    /**
     * 使用原始文档方式进行签名校验
     *
     * @param srccode    未签名的原文
     * @param signReturn 签名值
     */
    public static void JitFileVerify(String srccode, String signReturn) {
        DSign vSignDs = new DSign();
        vSignDs.initConfig(configPath);
        //1.获取原文-直接赋串
        //long verifyResult = vSignDs.verifyDetachedSign(srccode, "".getBytes(), signReturn);
        long verifyResult = vSignDs.verifyDetachedSign(srccode, "".getBytes(), signReturn);
        if (verifyResult == 0) { //返回0代表验证成功
            System.out.println("************Detach验证成功,返回证书信息*****************");
            System.out.println("版本号:" + vSignDs.getCertInfo("VS", 3, ""));
            System.out.println("颁发者:" + vSignDs.getCertInfo("VS", 1, ""));
            System.out.println("证书主题:" + vSignDs.getCertInfo("VS", 0, ""));
            System.out.println("验签返回的摘要算法为：" + vSignDs.getRndigestAlgID());

            //签名结果带时间戳
            DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", new DateFormatSymbols(Locale.SIMPLIFIED_CHINESE));
            String signedTimeStr = "";
            if (vSignDs.getTsaTime() != null) {
                signedTimeStr = formatter.format(vSignDs.getTsaTime());
            }
            System.out.println("时间: " + signedTimeStr);
            System.out.println("证书subjectdn: " + vSignDs.getTsaCertInfo("subjectdn"));
            System.out.println("证书issure： " + vSignDs.getTsaCertInfo("issuerdn"));
            System.out.println("证书SN： " + vSignDs.getTsaCertInfo("serialnumber"));
        } else {
            System.out.println("错误号为:" + vSignDs.getErrorCode());
            System.out.println("错误描述:" + vSignDs.getErrorMessage());
        }
    }


    /**
     * 使用 byte数组方式进行签名校验
     *
     * @param srccode    未签名的原文
     * @param signReturn 签名值
     */
    public static void JitByteVerify(byte[] srccode, byte[] signReturn) {
        DSign vSignDs = new DSign();
        vSignDs.initConfig(configPath);
        //1.获取原文-直接赋串
        long verifyResult = vSignDs.verifyDetachedSign(signReturn, srccode);
        if (verifyResult == 0) { //返回0代表验证成功
            System.out.println("************Detach验证成功,返回证书信息*****************");
            System.out.println("版本号:" + vSignDs.getCertInfo("VS", 3, ""));
            System.out.println("颁发者:" + vSignDs.getCertInfo("VS", 1, ""));
            System.out.println("证书主题:" + vSignDs.getCertInfo("VS", 0, ""));
            System.out.println("验签返回的摘要算法为：" + vSignDs.getRndigestAlgID());

            //签名结果带时间戳
            DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", new DateFormatSymbols(Locale.SIMPLIFIED_CHINESE));
            String signedTimeStr = "";
            if (vSignDs.getTsaTime() != null) {
                signedTimeStr = formatter.format(vSignDs.getTsaTime());
            }
            System.out.println("时间: " + signedTimeStr);
            System.out.println("证书subjectdn: " + vSignDs.getTsaCertInfo("subjectdn"));
            System.out.println("证书issure： " + vSignDs.getTsaCertInfo("issuerdn"));
            System.out.println("证书SN： " + vSignDs.getTsaCertInfo("serialnumber"));
        } else {
            System.out.println("错误号为:" + vSignDs.getErrorCode());
            System.out.println("错误描述:" + vSignDs.getErrorMessage());
        }
    }


    public static byte[] toByteArray(InputStream input) throws IOException {
        byte[] buffer = new byte[1024];
        int read;
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        while ((read = input.read(buffer)) != -1) {
            output.write(buffer, 0, read);
        }
        return output.toByteArray();
    }


    public static void main(String[] args) throws IOException {
        String OUT_DIR = "target/test-output/";
        //itextVerify(OUT_DIR + "sign_me_1236.pdf");
        //pdfBoxVerify(OUT_DIR + "sign_me_1236.pdf");
        //JitFileVerify("D:\\3334.pdf", "D:\\dsrc.txt");
        pdfBoxVerify(OUT_DIR + "sign_me_1236.pdf");

    }


}
